﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Reflection;

namespace Game
{
    class HTMLBlock
    {
        private String mBlockName;
        private String[] mAttirb;

        public HTMLBlock(String name, String[] attirb)
        {
            mBlockName = name;
            mAttirb = attirb;
        }
        public String GetName { get{ return mBlockName; } }
        public String[] GetAttrib { get { return mAttirb; } }
    }

    class HTMLWriter : StreamWriter
    {
        public HTMLWriter(String path)
            : base(path)
        {
            AutoFlush = true;
        }

        public void Enter(HTMLBlock block)
        {
            String src = String.Format("<{0}", block.GetName);
            if (block.GetAttrib != null)
            {
                foreach (String it in block.GetAttrib)
                {
                    src += " " + it;
                }
            }
            WriteLine(src+">");
        }

        public void Leave(HTMLBlock block)
        {
            WriteLine(String.Format("</{0}>", block.GetName));
        }

        public void WriteText(String text)
        {
            WriteLine(text);
        }
    }

    /*
     */
    static public class Log
    {
        public delegate void MessageHandler(String text);

        static public event MessageHandler Message;

        static private String mLogDir;
        static private String mLogCurr;
        static public String mLogFile;
        static private HTMLWriter mLog = null;

        static private String[] mAHint = { "color=#66cc00" };
        static private String[] mAWarn = { "color=#FFCD00" };
        static private String[] mAError = { "color=#660000" };

        static private HTMLBlock mHint = new HTMLBlock("font", mAHint);
        static private HTMLBlock mWarn = new HTMLBlock("font", mAWarn);
        static private HTMLBlock mError = new HTMLBlock("font", mAError);

        static private HTMLBlock mList = new HTMLBlock("li", null);

        static private String[] mAOL = { "type=1" }; //TODO: move to constructor
        static private String[] mAFont = { "size=2", "face=Consolas" };

        static private HTMLBlock[] mMainBlocks = { 
                                                     new HTMLBlock("html", null),
                                                     new HTMLBlock("body", null),
                                                     new HTMLBlock("font", mAFont),
                                                     new HTMLBlock("ol", mAOL)
                                                 };

        static public String GetFunction()
        {
            StackTrace stackTrace = new StackTrace(2, true);    // get call stack, skip internal call
            StackFrame[] stackFrames = stackTrace.GetFrames();  // get method calls (frames)
            if (stackTrace.FrameCount > 0)
            {
                StackFrame frame = stackFrames[0];
                MethodBase method = frame.GetMethod();

                return String.Format(@"{0} <b>file</b> <A href=""file:///{1}"">{2}</A> at <b>line</b> {3}",
                    method.ReflectedType.FullName,
                    frame.GetFileName(),
                    Path.GetFileName(frame.GetFileName()),
                    frame.GetFileLineNumber());
            }
            else
            {
                return "Unknown";
            }
        }

        static public void Init(String logDir, String logPath)
        {
            mLogDir = logDir;
            mLogCurr = logPath;
            String path = String.Format(@"{0}\{1}\{2}", Directory.GetCurrentDirectory(), logDir, logPath);
            Directory.CreateDirectory(path);
            mLogFile = String.Format(@"{0}\Log.html", path);
            mLog = new HTMLWriter(mLogFile);
            foreach (HTMLBlock it in mMainBlocks)
            {
                mLog.Enter(it);
            }
        }

        static public String GetLogTime()
        {
            DateTime now = System.DateTime.Now;
            return now.ToString(String.Format("hh:mm:ss:ms"));
        }

        static public void Debug(String text = "###[debug message]###")
        {
            Hint(text);
        }

        static public void Hint(String text)
        {
            mLog.Enter(mList);
            mLog.Enter(mHint);
            WriteText("[=]");
            mLog.Leave(mHint);
            WriteText(GetLogTime(), text, GetFunction());
            mLog.Leave(mList);
            //output intro console
            Console.WriteLine(text);
        }

        static public void ShowLogFile()
        {
            Process.Start(mLogFile);
        }

        static public void Error(Exception e)
        {
            Log.Error("Error message : " + e.Message);
            //get call stack
            String str = e.StackTrace.Replace(" at ", "<br/> at ");
            str = str.Replace(" in ", @"<A href=""file:///");
            str = str.Replace(":line", @"""> file </A> at <b>line</b>");
            Log.Error("StackTrace : " + str);
        }

        static public void Error(String text = "throw")
        {
            mLog.Enter(mList);
            mLog.Enter(mError);
            WriteText("[-]");
            mLog.Leave(mError);
            WriteText(GetLogTime(), text, GetFunction());
            mLog.Leave(mList);
            //output intro console
            Console.WriteLine(text);
        }

        static public void Warn(String text)
        {
            mLog.Enter(mList);
            mLog.Enter(mWarn);
            WriteText("[!]");
            mLog.Leave(mWarn);
            WriteText(GetLogTime(), text, GetFunction());
            mLog.Leave(mList);
            //output intro console
            Console.WriteLine(text);
        }

        static public void Close()
        {
            foreach (HTMLBlock it in mMainBlocks)//TODO: invert
            {
                mLog.Leave(it);
            }
        }

        static private void WriteText(String text)
        {
            mLog.WriteText(text);
            if (Message != null)
            {
                Message(text);
            }
        }

        static private void WriteText(String logTime, String text, String fn)
        {
            mLog.WriteText(String.Format("{0} : {1} : {2}", logTime, text, fn));
            if (Message != null)
            {
                Message(text);
            }
        }
    }
}
